package com.yichuang.security.jwt.main.config;

import com.alibaba.fastjson.JSONObject;
import com.yichuang.security.common.IgnoreUrl;
import com.yichuang.security.common.OAuth2Client;
import com.yichuang.security.common.ResultData;
import com.yichuang.security.common.ServerStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.web.AuthenticationEntryPoint;

/**
 * <p> oauth2资源服务器配置 </p>
 *
 * @author tuonioooo
 * @site https://miliqkdoc.motopa.cn/
 * @date 2018/8/22 14:20
 */
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        // AuthenticationEntryPoint用于处理未经认证的请求，通常会引导用户进行认证
        AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
        //自定义异常转换器，用于将OAuth2认证过程中的异常转换为特定的HTTP响应，这里通过 Auth2ResponseExceptionTranslator 来进行转换
        ((OAuth2AuthenticationEntryPoint) authenticationEntryPoint).setExceptionTranslator(new Auth2ResponseExceptionTranslator());
        resources.authenticationEntryPoint(authenticationEntryPoint);
        resources.resourceId(OAuth2Client.CLIENT_ID);//设置oauth_client_details表中的resource_ids字段资源
    }

    /**
     * access_token不存在/没有访问权限 异常处理
     * 说明：
     * Authentication异常处理过滤器
     * ExceptionTranslationFilter
     * 方法  1.handleSpringSecurityException 2.sendStartAuthentication
     * 本方法对过滤器处理后的后续处理，使返回信息更加人性化
     * 也可以通过实现AuthenticationEntryPoint 接口 commence方法处理
     *
     * @return
     */
    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return (request, response, exception) -> {
            // 判断异常类型，调整日志级别
            if (exception.getMessage().contains("Full authentication is required")) {
                log.info("http请求异常: 请求路径 [{}], 来源IP [{}], 异常信息 [{}]",
                        request.getRequestURI(),
                        request.getRemoteAddr(),
                        exception.getMessage());
            } else {
                log.error("http请求异常，请检查: [{}]", exception.getMessage());
            }
            response.setStatus(HttpStatus.OK.value());
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(JSONObject.toJSONString(ResultData.fail(ServerStatusEnum.UNAUTHORIZED.getCode(), ServerStatusEnum.UNAUTHORIZED.getDesc())));
        };
    }

    /**
     * TokenEndpointAuthenticationFilter  认证过滤器
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable() //关闭跨站请求防护
                .authorizeRequests()
                .antMatchers(IgnoreUrl.antMatchers()).permitAll()
                .anyRequest().authenticated() //除了上面匹配的规则之外的Url，都必须认证
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint())
                .and()
                .httpBasic();

    }

}
